package com.sinodata.bsm.cicp.collector.environment;

import org.apache.log4j.Logger;

import com.sinodata.bsm.cicp.exception.CollectorException;

/**
 *
 * @author 友联创新 hehy
 * @date Dec 18, 2008
 */
public class ElectriProtocol {
    private static Logger logger = Logger.getLogger(ElectriProtocol.class);
    public static byte SOI = 0x7E;// 起始位标志（START OF INFORMATION）
    // public byte VER;//通信协议版本号
    // public byte ADR;//设备地址描述（1～254，0、255保留）
    // public byte CID1;//控制标识码（设备类型描述）
    // public byte CID2;//命令信息:控制标识码（数据或动作类型描述） 响应信息:返回码RTN
    // public byte[2] LENGTH;//INFO字节长度（包括LENID和LCHKSUM）
    // public byte[n]INFO;//命令信息:控制数据信息COMMAND INFO 应答信息:应答数据信息DATA INFO
    // public byte[2] CHKSUM;//校验和码
    public static byte EOI = 0x0D;// 结束码

    public static byte[] getCommand(byte ver, byte adr, byte cid1, byte cid2, byte[] info) throws CollectorException {
        try {
            int infoLength = 0;
            if (info != null) {
                infoLength = info.length;
            }
            byte[] ret = new byte[infoLength * 2 + 18];
            byte[] command = new byte[infoLength + 6];
            byte[] LENGTH = calcLength(infoLength);

            command[0] = ver;
            command[1] = adr;
            command[2] = cid1;
            command[3] = cid2;
            command[4] = LENGTH[0];
            command[5] = LENGTH[1];
            if (info != null) {
                System.arraycopy(info, 0, command, 6, infoLength);
            }
            ret[0] = SOI;
            for (int i = 0; i < command.length; i++) {
                byte[] pair = convertASCII16TransCode(command[i]);
                ret[i * 2 + 1] = pair[0];
                ret[i * 2 + 2] = pair[1];
            }

            byte[] CHKSUM = calcChksum(ret);
            ret[infoLength * 2 + 13] = convertASCII16TransCode(CHKSUM[0])[0];
            ret[infoLength * 2 + 14] = convertASCII16TransCode(CHKSUM[0])[1];
            ret[infoLength * 2 + 15] = convertASCII16TransCode(CHKSUM[1])[0];
            ret[infoLength * 2 + 16] = convertASCII16TransCode(CHKSUM[1])[1];
            ret[infoLength * 2 + 17] = EOI;
            return ret;
        } catch (Exception e) {
            logger.error("failed to create command's transcode:  ver=" + ver + " adr=" + adr + " cid1=" + cid1 + " cid2=" + cid2, e);
            throw new CollectorException(e);
        }
    }

    public static byte[] getInfoBytes(byte[] resp) throws CollectorException {
        byte[] ret = null;
        if (resp.length <= 18) {
            ret = new byte[] {};
        } else {
            try {
                ret = new byte[(resp.length - 18) / 2];
                for (int i = 0; i < ret.length; i++) {
                    int h = convert2Int(resp[i * 2 + 13]);
                    int l = convert2Int(resp[i * 2 + 14]);
                    ret[i] = (byte) ((h << 4) | (l & 0xF));
                }
            } catch (Exception e) {
                ret = null;
                logger.error("failed to call ElectriProtocol.getInfoBytes()", e);
                throw new CollectorException(e);
            }
        }
        return ret;
    }

    /**
     * 根据info计算出LENGTH
     *
     * @param info
     * @return
     */
    public static byte[] calcLength(int len) {
        /*
         * LENGTH数据格式 高 字 节 低 字 节 校验码LCHKSUM 长度标示码LENID（表示INFO的传送中ASCII码字节数） D15
         * D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
         * LENGTH共2个字节，由LENID和LCHKSUM组成，LENID表示INFO项的ASCII码字节数，当LENID=0时，INFO为空，即无该项。LENGTH传输中先传高字节，再传低字节，分四个ASCII码传送。
         * 例: INFO项的ASCII码字节数为18，即LENID=0000 0001 0010B。
         * D11D10D9D8＋D7D6D5D4＋D3D2D1D0=0000B+0001B+0010B=0011B，模16余数为0011B，0011B取反加1就是1101B，即LCHKSUM为1101B。
         * 可得: LENGTH为1101 0000 0001 0010B，即D012H。
         */
        int lenid = len * 2;
        // 校验码的计算:D11D10D9D8＋D7D6D5D4＋D3D2D1D0，求和后模16余数取反加1
        int lch = ((lenid & 0xf00) >> 8) + ((lenid & 0xf0) >> 4) + lenid & 0xf;
        int lchksum = 1 + ~lch & 0xF;
        byte LCHKSUM = (byte) ((lchksum << 4) + ((lenid & 0xF00) >> 8));
        byte LENID = (byte) (lenid & 0xFF);
        return new byte[] { LCHKSUM, LENID };
    }

    public static byte[] calcChksum(byte[] retCmd) {
        int chksum = 0;
        for (int i = 1; i < retCmd.length - 5; i++) {
            chksum += retCmd[i];
        }
        chksum = 1 + ~(chksum % 0x10000);
        return new byte[] { (byte) (chksum >> 8), (byte) (chksum & 0xFF) };
    }

    /**
     * 十六进制解释，以ASCII码的方式传输，每个字节用两个ASCII码表示，即高四位用一个ASCII码表示，低四位用一个ASCII码表示
     *
     * @param origin
     * @return
     * @throws Exception
     */
    public static byte[] convertASCII16TransCode(byte origin) throws Exception {
        int high = convert2ASCII((origin & 0xF0) >> 4);
        int low = convert2ASCII(origin & 0xF);
        return new byte[] { (byte) high, (byte) low };
    }

    /**
     * 把十六进制 1~f 转到 ascii16进制
     *
     * @param hex
     * @return
     * @throws Exception
     */
    public static int convert2ASCII(int hex) throws Exception {
        if (hex >= 0 && hex <= 9) {
            return hex + 48;
        } else if (hex >= 10 && hex <= 15) {
            return hex + 55;
        }
        throw new Exception("输入参数:" + hex + " 超出范围[0,15]");
    }

    /**
     * 把ascii16进制 1~f 转到十六进制
     *
     * @param ascii16
     * @return
     * @throws Exception
     */
    public static int convert2Int(int ascii16) throws Exception {
        if (ascii16 >= 48 && ascii16 <= 57) {
            return ascii16 - 48;
        } else if (ascii16 >= 65 && ascii16 <= 70) {
            return ascii16 - 55;
        }
        throw new Exception("输入参数:" + ascii16 + " 超出范围['0','9']U['a','f']");
    }

    /**
     * 把两个ascii传输码16进制解释成一个byte
     * @param high
     * @param low
     * @return
     */
    public static byte unite2ASCII(byte high, byte low) {
        int h;
        int l;
        try {
            h = convert2Int(high);
            l = convert2Int(low);
            return (byte) ((h << 4) | l);
        } catch (Exception e) {
            Logger.getLogger(ElectriProtocol.class).error(e.getMessage());
        }
        return 0;
    }

    /**
     * 把两个byte，以一个为高8位，一个以第八位，合并成一个16位的short int
     *
     * @param high
     * @param low
     * @return
     */
    public static int mergeInt(byte high, byte low) {
        return (high << 8) | (low & 0xFF);
    }

    public static float mergeFloat(byte d7_0, byte d15_8, byte d23_16, byte d31_24) {
        //        int s = ((d31_24 >> 7) == 0) ? 1 : -1;
        float m = d7_0 | (d15_8 << 8) | ((d23_16 << 16) & 0x6F0000);
        int e = ((d31_24 << 1) & 0xFF) | ((d23_16 >> 7) & 0x1);
        m = 1 + m / (1 << 23);
        return m * (1 << (e - 127));
    }

    public static void main(String[] args) {
        try {
            byte[] b2 = ElectriProtocol.convertASCII16TransCode((byte) 0x4B);
            System.out.println(b2[0]);
            System.out.println(b2[1]);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
